<?php
/**
 * @package Curly
 * @version 0.9.1
 * @link http://curly.codeplex.com/
 * @license http://curly.codeplex.com/license The MIT License
 * @author Dawid Zawada
 */

namespace Curly
{
    use Curly\Cookies\Cookie;
	use Curly\Cookies\CookieContainer;
    use Curly\Collections\StringIndexedCollection;

    /**
     * Class representing a HTTP response
     * 
     * @package Curly
     * @license http://curly.codeplex.com/license The MIT License
     */
	class Response
    {
        /**
         * Final URI (after redirections)
         * 
         * @var string
         */
        protected $uri;
        
        /**
         * Content of the response
         * 
         * @var string
         */
        protected $content;
        
        /**
         * Path to the output file (to which the content
         * was written)
         * 
         * @var string
         */
        protected $outputFilePath;
        
        /**
         * HTTP status of the response
         * 
         * @var Curly\HttpStatus
         */
        protected $status;
        
        /**
         * Character set of the response
         * 
         * @var string
         */
        protected $charset;
        
        /**
         * Type of the response content
         * 
         * @var string
         */
        protected $contentType;
        
        /**
         * Length of the response content
         * 
         * @var int
         */
        protected $contentLength;
        
        /**
         * Cookies from the response headers
         * 
         * @var Curly\Cookies\CookieContainer
         */
        protected $cookies;
        
        /**
         * Headers from the response
         * 
         * @var Curly\Collections\StringIndexedCollection
         */
        protected $headers;
        
        /**
         * Creates a new Reponse object
         *
         * @internal
         */
        public function __construct()
        {
            $this->cookies = new CookieContainer();
            $this->headers = new StringIndexedCollection();
        }
        
        /**
         * Returns the final URI
         *
         * @return string
         */
        public function getUri()
        {
            return $this->uri;
        }
        
        /**
         * Sets the final URI
         * 
         * @internal
         * @param string $uri
         */
        public function _setUri( $uri )
        {
            $this->uri = $uri;
        }
        
        /**
         * Returns the response content
         *
         * @return string
         */
        public function getContent()
        {
            return $this->content;
        }
        
        /**
         * Sets the response content
         * 
         * @internal
         * @param string $content
         */
        public function _setContent( $content )
        {
            $this->content = $content;
        }
        
        /**
         * Returns the output file path
         *
         * The output file is a file to which the content was written
         * 
         * @return string
         */
        public function getOutputFilePath()
        {
            return $this->outputFilePath;
        }
        
        /**
         * Sets the output file path
         * 
         * The output file is a file to which the content was written
         * 
         * @internal
         * @param string $outputFilePath
         */
        public function _setOutputFilePath( $outputFilePath )
        {
            $this->outputFilePath = $outputFilePath;
        }
        
        /**
         * Returns the response HTTP status 
         *
         * @return Curly\HttpStatus
         */
        public function getStatus()
        {
            return $this->status;
        }
        
        /**
         * Sets the response HTTP status
         * 
         * @param Curly\HttpStatus $status
         */
        protected function setStatus( HttpStatus $status )
        {
            $this->status = $status;
        }
        
        /**
         * Returns the character set of the response content
         *
         * @return string
         */
        public function getCharset()
        {
            return $this->charset;
        }
        
        /**
         * Sets the character set of the response content
         * 
         * @param string $charset
         */
        protected function setCharset( $charset )
        {
            $this->charset = (string)$charset;
        }
        
        /**
         * Returns the type of the response content
         *
         * @return string
         */
        public function getContentType()
        {
            return $this->contentType;
        }
        
        /**
         * Sets the type of the response content
         * 
         * @param string $contentType
         */
        protected function setContentType( $contentType )
        {
            $this->contentType = (string)$contentType;
        }
        
        /**
         * Returns the length of the response content
         *
         * @return int
         */
        public function getContentLength()
        {
            return $this->contentLength;
        }
        
        /**
         * Sets the length of the response content
         * 
         * @param mixed $contentLength
         */
        protected function setContentLength( $contentLength )
        {
            if( $contentLength !== null )
            {
                $contentLength = (int)$contentLength;
            }
            
            $this->contentLength = $contentLength;
        }
        
        /**
         * Collection of cookies read from the response headers
         *
         * @return Curly\Cookies\CookieContainer
         */
        public function cookies()
        {
            return $this->cookies;
        }
        
        /**
         * Collection of headers read from the response
         *
         * This collection does not contain:
         * - status header
         * - Content-Type header
         * - Content-Length header
         * - Set-Cookie headers
         * Use this objects getters to acquire values of those headers.
         * 
         * @return Curly\Collections\StringIndexedCollection
         */
        public function headers()
        {
            return $this->headers;
        }
        
        /**
         * Callback method for the cURL library, used to read 
         * response headers
         * 
         * @see http://us.php.net/curl_setopt (CURLOPT_HEADERFUNCTION)
         * @internal
         * @param resource $ch
         * @param string $headerLine
         */
        public function _headerLineCallback( $ch, $headerLine )
        {
            static $headersEnd = false;
            
            if( $headerLine == "\r\n" )
            {
                // End of headers
                $headersEnd = true;
            }
            else if( $headersEnd )
            {
                // Another set of headers (redirection occured)
                $this->reset();
                $headersEnd = false;
                
                // HTTP status
                if( preg_match( "~^HTTP/1\\.[0-9]\\s*(?<code>[0-9]{3})~i", $headerLine, $match ) )
                {
                    $this->setStatus( HttpStatus::_fromCode( $match["code"] ) );
                }
            }
            else 
            {
                // HTTP status
                if( preg_match( "~^HTTP/1\\.[0-9]\\s*(?<code>[0-9]{3})~i", $headerLine, $match ) )
                {
                    $this->setStatus( HttpStatus::_fromCode( $match["code"] ) );
                } 
                // Cookies
                else if( stripos( $headerLine, "Set-Cookie:" ) === 0 )
                {
                    $this->cookies()->add( Cookie::parse( $headerLine ) );
                }
                // Content-Type
                else if( preg_match( "~^Content-Type:\\s*(?<contentType>[^;]*)(?:;\\s*charset\\s*=\\s*(?<charset>\\S*))?~i", $headerLine, $match ) )
                {
                    $this->setContentType( $match["contentType"] );
                    if( isset( $match["charset"] ) )
                    {
                        $this->setCharset( $match["charset"] );
                    }
                }
                // Content-Length
                else if( preg_match( "~^Content-Length:\\s*(?<length>[0-9]+)~i", $headerLine, $match ) )
                {
                    $this->setContentLength( $match["length"] );
                }
                // Other headers
                else
                {
                    $header = explode( ":", $headerLine, 2 );
                    $name = trim( $header[0] );
                    $value = count( $header ) == 2 ? trim( $header[1] ) : null;
                    
                    $this->headers()->set( $name, $value );
                }
            }
            
            // Returning length of the $headerLine param (action required by the CURLOPT_HEADERFUNCTION)
            return strlen( $headerLine );
        }
        
        /**
         * Clears all headers (except cookies)
         */
        protected function reset()
        {
            $this->headers()->clear();
            $this->setContentType( null );
            $this->setCharset( null );
            $this->setContentLength( null );
        }
    }
}